/**
 * 开发者   : Marco
 * 创建时间 : 2016年7月18日, 上午9:35
 */


#include "reponse.h"

#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <marco/marco.h>
#include <marco/convert.h>

#ifndef MARCO_NO_HTTP
MARCOPP_NAMESPACE_BEGIN
MARCOPP_NAMESPACE_HTTP_BEGIN

/**
 * 隐藏实现类
 */
class reponse_impl {
public:
    explicit reponse_impl(status_type status) : status_(status), data_(NULL), len_(0) { } 
    status_type get_status(void) const { return status_; }
    void set_status(status_type status) { this->status_ = status; }
    
    void set_direct_data(void* ptr, int len) {	// 设置直接数据, 之后发送时不再组成http头, 正文, 而直接发送这里的内容, 完全由用户组成数据.
	if (data_) free(data_);
	data_ = ptr;
	len_ = len;
    }
    void* get_direct_data(int& len) { len = len_; return data_; }   // 获取直接数据
     
private:
    status_type status_;	// 回复状态
    void* data_;		// 直接数据
    int len_;			// 直接数据长度
};



reponse::reponse(version_type version, status_type status) : basic(version), impl(new reponse_impl(status)) { }
reponse::~reponse(void) { delete impl; }

status_type reponse::get_status(void) const { return impl->get_status(); }
void reponse::set_status(status_type status) { impl->set_status(status); }

void reponse::set_cur_date(void) { char tmp[30]; set_msghead("Date", get_http_datetime(tmp)); }  // 设置协议头中Date行, 使用当前时间
void reponse::set_datetime(time_t timesec) { char tmp[30]; set_msghead("Date", timesec_to_httpdatetime(timesec, tmp)); } // 设置协议头中Data行, 使用指定的时间

void reponse::set_server(const char* server_str) { set_msghead("Server", server_str); }		// 设备服务器名称
void reponse::set_location(const char* location_str) { set_msghead("Location", location_str); }	// 设置跳转目标地址

void reponse::set_expires(time_t expires_timesec) { char tmp[30]; set_msghead("Expires", timesec_to_httpdatetime(expires_timesec, tmp)); }	// 根据时间秒设置缓存时间
void reponse::set_expires(const char* expires_str) { set_msghead("Expires", expires_str); }    // 设置缓存时间, 已设置好的格式


/**
 * 解析http数据, 数据必须包括整个http协议头, 允许只有部分正文, 解析发现与头中描述的Content-Length不符时, 返回http_parse_again
 * @param data 协议数据原始内容, 解析过程中会修改部分位置, 无论解析成功或失败, 此数据不得再使用.
 * @param data_len (in/out)数据长度, 返回未完成时(http_parse_again), 此变量返回还需多少字节.
 * @return 解析结果, 可能(完成,未完成,失败)
 */
parse_result reponse::parse(char* data, int* data_len) {
    char *ptr, *status_ptr, *content_ptr;
    printf("start reponse parse:\n%s\n", data);

    if (!(content_ptr = strstr(data, "\r\n\r\n"))) // 没有完整的http报文头
	return parse_failed;

    /** ========================================
     * Step: 消息报头
     */
    *(content_ptr + 2) = 0; // 多留一个\r\n方便head解析
    content_ptr += 4; // 指向正文

    if ((ptr = strstr(data, "\r\n"))) {
	*ptr = 0;
	ptr += 2; // 指向第一个head头
	if (!init_msghead(ptr)) return parse_failed_head; // 初始化消息报头, 存为键值对
    }
    
    /** ========================================
     * Step: HTTP版本  
     */
    if (!(status_ptr = strchr(data, ' '))) // 查找第一个空格, 取版本
	return parse_failed_version;
    *status_ptr++ = 0;			    // 隔开version与status
    
    if (!strcmp(data + 5, "1.1")) {
	set_version(version_1_1);
    } else if (!strcmp(data + 5, "1.0")) {
	set_version(version_1_0);
    } else {
	return parse_failed_version;
    }

    /** ========================================
     * Step: 取状态值
     */
    if (!(ptr = strchr(status_ptr, ' '))) {
	return parse_failed_status;
    }
    
    *ptr++ = 0;
    impl->set_status((status_type)strtol(status_ptr, NULL, 10));

    /** ========================================
     * Step: 可能有正文未传送完, 检查是否未处理完
     */
    const char* len_ptr = get_msghead("Content-Length");
    int cur_content_len = *data_len - (content_ptr - data); // 当前正文长度

    if (cur_content_len > 0) { // 有正文内容
	append_content_str(content_ptr, cur_content_len); // 添加正文
    }

    if (len_ptr) {
	//		printf("Content-Length:%s, current body len:%d.\n", len_ptr, body_len);
	int total_content_len = (int)strtol(len_ptr, NULL, 10);		// 正文总长度
	if (cur_content_len == total_content_len) return parse_success; // 当前全部读完

	*data_len = total_content_len - cur_content_len; // 告诉调用处, 还需要读多少字节
	if (*data_len < 0) return parse_failed_length; // 不能比当前的还小.
	return parse_again;

    } else { // head中没有指示长度, 不管当前是否有正文内容, 认为成功
	return parse_success;
    }

    return parse_success;
}

/**
 * 获取http头字符串, 包括状态行与消息报头
 * @param buf (out)存储区
 */
void reponse::create_head_str(string& buf) {
    char status_buf[200];
    string msghead_str;
    
    create_status_str(status_buf);	// 状态行
    buf = status_buf;
    get_msghead_str(msghead_str);
    buf += msghead_str;
    buf += "\r\n";	// http头结束换行符
}

/**
 * 获取http头字符串, 包括状态行与消息报头
 * @param buf (out)存储区
 * @return 总长度
 */
int reponse::create_head_str(char* buf) {
    int len, ret;
    
    len = create_status_str(buf);	    // 状态行
    ret = get_msghead_str(buf+len); // 各个消息报文头
    len += ret;
    buf[len++] = '\r';	    // http头结束换行符
    buf[len++] = '\n';
    buf[len] = 0;
    
    return len;
}

/** 获取状态行字符串形式, 添加\r\n换行符, 添加\0结束符
 */
int reponse::create_status_str(char* buf) {
    int len, ret;
    
    // 版本号
    len = sprintf(buf, "HTTP/1.");
    version_type version = get_version();
    if (version == version_1_1) buf[len++] = '1';
    else buf[len++] += '0';

    // 状态值
    status_type status = get_status();
    buf[len++] = ' ';
    ret = sprintf(buf+len, "%d", int(status));
    len += ret;
    buf[len++] = ' ';

    // 状态文字
    if (status == ok) {
	ret = sprintf(buf+len, "OK\r\n");
	len += ret;
    } else if (status == not_found) {
	ret = sprintf(buf+len, "NOT FOUND\r\n");
	len += ret;
    } else if (status == internal_server_error) {
	ret = sprintf(buf+len, "Internal Server Error\r\n");
	len += ret;
    } else {
	ret = sprintf(buf+len, "Unknow\r\n");
	len += ret;
    }
    
    return len;
}

void reponse::set_direct_data(void* ptr, int len) {
    impl->set_direct_data(ptr, len);
}

void* reponse::get_direct_data(int& len) {
    return impl->get_direct_data(len);
}


MARCOPP_NAMESPACE_HTTP_END
MARCOPP_NAMESPACE_END
#endif // MARCO_NO_HTTP